package com.cpit.platform.commontable.service.impl;

import com.cpit.platform.common.dao.SimpleExpression;
import com.cpit.platform.common.entity.Result;
import com.cpit.platform.common.entity.TableDataQueryCondition;
import com.cpit.platform.common.entity.TableJsonData;
import com.cpit.platform.common.excel.ExportExcel;
import com.cpit.platform.dto.BaseEntity;
import com.cpit.platform.log.LogOperator;
import com.cpit.platform.commontable.dao.CommonTableRepository;
import com.cpit.platform.commontable.service.CommonTableService;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;

@Service("CommonTableServiceImpl")
@Transactional(rollbackFor = Exception.class)
public class CommonTableServiceImpl implements CommonTableService {
    private final String loggerName = this.getClass().getName();
    @Autowired
    private CommonTableRepository entityRepository;
    protected CommonTableRepository getEntityRepository(){
        return  entityRepository;
    }
    @Override
    public Result<Map> create(String tableName,Map data){
        LogOperator.debug(loggerName, "create data = "+data);
        Result<Map> result;
        try {
            String msg = beforeCreate(tableName,data);
            if((!StringUtils.isEmpty(msg))){
                return new Result<>(Result.CODE_FAILED, msg, data);
            }
            //创建之后才有id
            Map savedData = (Map)this.getEntityRepository().save(tableName,data);
            this.doAfterCreated(data, savedData);
            result = new Result<>(Result.CODE_SUCCESS, "", savedData);
        } catch (Exception e) {
            LogOperator.err(loggerName, "create error", e);
            result = new Result<>(Result.CODE_FAILED, e.getMessage(), data);
        }
        LogOperator.debug(loggerName, "create result = "+result);
        return result;
    }

    @Override
    public  Result<Map> modify(String tableName,Map data) {
        Long id = this.getIdFromEntity(data);
        return this.modify(tableName,id,data);
    }

    @Override
    public Result<Map> modify(String tableName,Long id, Map<String, Object> partFieldValueMap) {
        LogOperator.debug(loggerName, "modify data: tableName = " + tableName + ",id = " + id + ",partFieldValueMap = " + partFieldValueMap);
        Map dataInDB = this.findById(tableName,id);
        if (dataInDB == null) {
            return new Result<>(Result.CODE_FAILED, "要修改的" + tableName + "不存在");
        }
        Map data = partFieldValueMap;
        LogOperator.debug(loggerName, "modify data = "+data);
        Result<Map> result = null;
        try {
            String msg = beforeModify(tableName,data);
            if((!StringUtils.isEmpty(msg))){
                return new Result<>(Result.CODE_FAILED, msg, data);
            }
            Integer modifyReturn =  this.getEntityRepository().modify(tableName,id,data);
            if(modifyReturn == 1){
                Map savedData = this.findById(tableName,id);
                this.doAfterModified(data, savedData);
                result = new Result<>(Result.CODE_SUCCESS, "", savedData);
            }else{
                result = new Result<>(Result.CODE_FAILED, "modify return not 1", data);
            }

        } catch (Exception e) {
            LogOperator.err(loggerName, "modify error", e);
            result = new Result<>(Result.CODE_FAILED, e.getMessage(), data);
        }
        LogOperator.debug(loggerName, "modify result = "+result);
        return result;
    }

    @Override
    public Result<Map>  delete(String tableName,Map data){
        LogOperator.debug(loggerName, "delete data = "+data);
        Result<Map> result = null;
        try {
            if (data == null) {
                throw new Exception("参数为空");
            }
            Long deleteId = getIdFromEntity(data);
            if(deleteId == null){
                throw new Exception("参数ID为空");
            }
            Map dt = findById(tableName,deleteId) ;
            if (dt == null){
                throw new Exception("删除的"+tableName+"不存在:" + data);
            }
            this.doBeforeDeleted(dt);
            this.getEntityRepository().delete(tableName,dt,null);
            result = new Result<>(Result.CODE_SUCCESS, "", data);
        } catch (Exception e) {
            LogOperator.err(loggerName, "delete error", e);
            result = new Result<>(Result.CODE_FAILED, e.getMessage(), data);
        }
        LogOperator.debug(loggerName, "modify result = "+result);
        return result;
    }

    @Override
    public Result<Map>  delete(String tableName,Long id){
        LogOperator.debug(loggerName, "delete id = "+id);
        Result<Map> result = null;
        try {
            Map data = findById(tableName,id) ;
            if (data == null){
                throw new Exception("删除的"+tableName+"不存在:" + data);
            }
            this.doBeforeDeleted(data);
            getEntityRepository().delete(tableName,id);
        } catch (Exception e) {
            LogOperator.err(loggerName, "delete error", e);
        }
        LogOperator.debug(loggerName, "delete ok");
        return null;
    }

    @Override
    public List<Map> findAll(String tableName) {
        List<Map> result = null;
        try {
            result = getEntityRepository().findAll(tableName);
        } catch (Exception e) {
            LogOperator.err(loggerName, "findAll error", e);
            result = Collections.EMPTY_LIST;
        }
        LogOperator.debug(loggerName, "findAll result = "+result);
        return result;
    }

    @Override
    public Map findById(String tableName,Long id) {
        LogOperator.debug(loggerName, "findById id = "+id);
        List<Map> resultList =( List<Map>) getEntityRepository().findByColumnEqual(tableName,"id",id);
        Map result = Collections.EMPTY_MAP;
        if(resultList != null && resultList.size() > 0){
            result = resultList.get(0);
        }
        LogOperator.debug(loggerName, "findById result = "+result);
        return result;
    }

    @Override
    public List<Map> findByQueryCondition(String tableName,Map<String, Object> queryCondMap) {
        List<Map> result = findByQueryCondition(tableName,queryCondMap ,null);
        return result;
    }

    @Override
    public List<Map> findByQueryCondition(String tableName,List<SimpleExpression> queryCondSimple) {
        List<Map> result =  findByQueryCondition( tableName ,null,queryCondSimple);
        return result;
    }
    @Override
    public List<Map> findByQueryCondition(String tableName,SimpleExpression simpleExpression) {
        List<SimpleExpression> queryCondSimple = new ArrayList<SimpleExpression>();
        queryCondSimple.add(simpleExpression);
        List<Map> result =  findByQueryCondition( tableName,null ,queryCondSimple);
        return result;
    }
    @Override
    public Page<Map>  findByQueryCondition(String tableName,int page, int size, String order, String orderBy, Map<String, Object> queryCondMap) {
        Sort.Direction der = Sort.Direction.ASC.name().equalsIgnoreCase(order) ? Sort.Direction.ASC : Sort.Direction.DESC;
        PageRequest pageable = new PageRequest(page - 1, size, new Sort(der, orderBy));
        return findByQueryCondition( tableName,queryCondMap ,null, pageable);
    }

    @Override
    public Page<Map> findByQueryCondition(String tableName,int page, int size, String order, String orderBy, List<SimpleExpression> queryCondSimple) {
        Sort.Direction der = Sort.Direction.ASC.name().equalsIgnoreCase(order) ? Sort.Direction.ASC : Sort.Direction.DESC;
        PageRequest pageable = new PageRequest(page - 1, size, new Sort(der, orderBy));
        return findByQueryCondition( tableName,null, queryCondSimple, pageable);
    }

    @Override
    public TableJsonData findByTableQueryCondition(String tableName,TableDataQueryCondition tableDataQueryCondition) {
        PageRequest pageable = TableDataQueryCondition.getPageRequestByTableDataQueryCondition(tableDataQueryCondition);
        //一、处理原有Map查询条件,将其放入简单条件列表中统一处理
        Map queryCondMap = tableDataQueryCondition.getQueryCond();
        List<SimpleExpression> queryCondSimple = tableDataQueryCondition.getQueryCondSimple();
        Page<Map> ret = findByQueryCondition(tableName, queryCondMap ,queryCondSimple, pageable);

        return new TableJsonData(tableDataQueryCondition, ret.getTotalElements(), ret.getContent());
    }

    @Override
    public String export(String tableName,HttpServletRequest request, HttpServletResponse response){
        try {
            String date = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
            String fileName = tableName + "_" + date + ".xlsx";
            List<Map> entityList;
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (parameterMap.isEmpty()) {
                entityList = getEntityRepository().findAll(tableName);
            } else {
                Map<String, Object> condMap = new HashMap<>();
                for (String key:parameterMap.keySet()) {
                    condMap.put(key, request.getParameter(key));
                }
                entityList = this.findByQueryCondition(tableName,condMap);
            }
            ExportExcel exportExcel =new ExportExcel(tableName, Map.class);
            exportExcel.setDatePattern("yyyy-MM-dd HH:mm:ss");
            exportExcel .setDataList(entityList).write(response, fileName).dispose();
        } catch (IOException e) {
            LogOperator.err(loggerName, "export info failed!", e);
        }
        return null;
    }

    protected Map doAfterCreated(Map data, Map returnedData) {
        return data;
    }

    protected Map doAfterModified(Map data, Map returnedData) {
        return data;
    }

    protected Map doBeforeDeleted(Map data) throws Exception {
        return data;
    }

    private List<Map>  findByQueryCondition( String tableName,Map queryCondMap ,List<SimpleExpression> queryCondSimple) {
        LogOperator.debug(loggerName, "findByQueryCondition queryCondMap = "+queryCondMap+",queryCondSimple = "+queryCondSimple);
        List<Map>  result = this.getEntityRepository().findByCondition(tableName,queryCondMap,queryCondSimple);
        LogOperator.debug(loggerName, "findByQueryCondition result = "+result);
        return result;
    }

    public Page<Map> findByQueryCondition(String tableName, Map queryCondMap ,List<SimpleExpression> queryCondSimple, Pageable pageable) {
        LogOperator.debug(loggerName, "findByQueryCondition queryCondMap = "+queryCondMap+",queryCondSimple = "+queryCondSimple+",pageable = "+pageable);
        Page<Map> result = this.getEntityRepository().findByCondition(tableName,queryCondMap,queryCondSimple,pageable);
        LogOperator.debug(loggerName, "findByQueryCondition result = "+result);
        return result;
    }


    protected  String beforeModify(String tableName,Map data) {
        String msg = checkParaNull(tableName,data,false);
        if(!StringUtils.isEmpty(msg)) {
            return msg;
        }
        Map curByIdInDb = findById(tableName,getIdFromEntity(data));
        if (curByIdInDb == null) {
            msg = "修改的"+tableName+"不存在";
            LogOperator.info(loggerName, msg + ":" + data);
            return msg;
        }

        Long id = this.getIdFromEntity(data);
        msg = this.persistedObjectFieldDuplicated(tableName,data, id);
        if(!StringUtils.isEmpty(msg)){
            return msg;
        }

        msg = this.persistedObjectUniqueConstraint(tableName,data, id);
        if(!StringUtils.isEmpty(msg)){
            return msg;
        }
        data.put("gmt_create",curByIdInDb.get("gmt_create"));
        data.put("gmt_modified","\""+formatDate(new Date())+"\"");
        return msg;
    }

    protected  String beforeCreate(String tableName,Map data) {
        String msg = checkParaNull(tableName,data,true);
        if(!StringUtils.isEmpty(msg)){
            return msg;
        }

        Long id = this.getIdFromEntity(data);
        if ( id != null && id > 0){
            LogOperator.info(loggerName, "id已赋值");
        }
        id = 0L;
        setId(data, id);

        msg = this.persistedObjectFieldDuplicated(tableName,data, id);
        if(!StringUtils.isEmpty(msg)){
            return msg;
        }

        msg = this.persistedObjectUniqueConstraint(tableName,data, id);
        if(!StringUtils.isEmpty(msg)){
            return msg;
        }
        String date = formatDate(new Date());
        data.put("gmt_create",date);
        data.put("gmt_modified",date);
        return msg;
    }

    private String formatDate(Date date){
        return DateFormatUtils.format(date, "yyyy/MM/dd");
    }
    /**
     * 对非空属性的合法性检查
     * @param data 实体类对象
     * @return 存在输入非法时返回提示信息，正常情况下返回null
     */
    protected String checkParaNull(String tableName,Map data,boolean checkAll){
        String entityDisName = tableName;
        if (data == null) {
            return entityDisName + "参数为空，请补充！";
        }
        String msg = persistedObjectContainsNull(tableName,data,checkAll);
        if (!StringUtils.isEmpty(msg) ) {
            return entityDisName + "输入信息不全，如下属性为空：" + msg;
        }

        return null;
    }


    private void setId(Map data, long value) {
            try {
                data.put("id", value);
            } catch (Exception e){
                LogOperator.err(loggerName, "setId error", e);
            }
    }

    private Long getIdFromEntity(Map data) {
        Long id = 0L;
            try {
                id = Long.parseLong(data.get("id").toString());
            } catch (Exception e){
                LogOperator.err(loggerName, "getIdFromEntity error", e);
            }
        return id;
    }

    /**
     * 对非空属性的合法性检查
     * @param obj 实体类对象
     * @return 存在输入非法时返回提示信息，正常情况下返回null
     */
    protected String persistedObjectContainsNull(String tableName,Map obj,boolean checkAll) {
        if(obj == null){
            return "对象为空";
        }
        StringBuilder sb = new StringBuilder();
        // 对象属性的Field
        checkObjectContainsNull(tableName,sb ,obj,checkAll);
        if(sb.length() > 0){
            sb.deleteCharAt(sb.length()-1);
        }
        return sb.toString();
    }

    private void checkObjectContainsNull(String tableName,StringBuilder sb ,Map obj,boolean checkAll){
        List<Map> columnMetaList =  this.getEntityRepository().getColumnMetaList(tableName);
        for (Map columnMeta : columnMetaList) {
                if("Yes".equalsIgnoreCase((String)columnMeta.get("Null"))){
                    continue;
                }
                String fieldDispName =(String)columnMeta.get("Field");;
                Object fieldValue = obj.get(fieldDispName);
                if(checkAll){
                    if (fieldValue == null) {
                        sb.append(fieldDispName).append(";");
                    }
                }else{
                    if(obj.containsKey(fieldDispName) && fieldValue == null){
                        sb.append(fieldDispName).append(";");
                    }
                }
        }
    }
    private boolean isBaseJavaType(Field field){
        Type type = field.getType();
        boolean isBaseJavaType = false;
        if (type == int.class
                || type == Integer.class
                || type == long.class
                || type == Long.class
                || type == double.class
                || type == Double.class
                || type == byte.class
                || type == Byte.class
                || type == boolean.class
                || type == Boolean.class
                || type == float.class
                || type == Float.class
                || type == short.class
                || type == Short.class
                || type == String.class
                ) {
            isBaseJavaType = true;
        }
        return isBaseJavaType;
    }
    /**
     * 对联合唯一性约束UniqueConstraint的合法性检查
     * @param obj 实体类对象
     * @return 存在输入非法时返回提示信息，正常情况下返回null
     */
    protected String persistedObjectUniqueConstraint(String tableName,Map obj, Long id) {
//        Class objClass = obj.getClass();
//        Table classEntityMeta = (Table) objClass.getAnnotation(Table.class);
//        if(classEntityMeta == null) {
//            return null;
//        }
//        for (UniqueConstraint uniqueConstraint:classEntityMeta.uniqueConstraints()) {
//            String[] columnNames = uniqueConstraint.columnNames();
//            StringBuilder sb = new StringBuilder("已存在以下信息相同的记录：");
//            Map<String, Object> queryCondMap = new HashMap<>();
//            for (String fieldName:columnNames) {
//                Object fieldValue = Reflections.getFieldValue(obj, fieldName);
//                sb.append(this.getFieldDisplayName((Map) obj, fieldName)).append("+");
//                queryCondMap.put(fieldName, fieldValue);
//            }
//            List<Map> list = findByQueryCondition(tableName,queryCondMap);
//            if (!list.isEmpty() && !id.equals(getIdFromEntity(list.get(0)))) {
//                return sb.deleteCharAt(sb.length()-1).toString();
//            }
//        }

        return null;
    }

    /**
     * 对违反单属性唯一性约束Unique的合法性检查
     * @param obj 实体类对象
     * @param id 源实体类对象的主键值
     * @return 存在输入非法时返回提示信息，正常情况下返回null
     */
    protected String persistedObjectFieldDuplicated(String tableName,Map obj, Long id) {
//        Class objClass = obj.getClass();
//        // 对象属性的Field
//        List<Field> propertyFields = getClassFields(objClass);
        List<Map> columnMetaList =  this.getEntityRepository().getColumnMetaList(tableName);
        for (Map columnMeta : columnMetaList) {
            try {
//(String)columnMeta.get("Field")

                String fieldName = (String)columnMeta.get("Field");
                Object fieldValue = obj.get( fieldName);
                String fieldDisplayName = fieldName;
                Class fieldType = this.getEntityRepository().getColumnType(columnMetaList,fieldName);

                if (String.class.isAssignableFrom(fieldType) && fieldValue != null) {
                    String type = (String)columnMeta.get("Type");
                    int leftPos = type.indexOf("(");
                    int rightPos = type.indexOf(")");
                    int allowLength =255;
                    if(leftPos != -1 && rightPos != -1){
                        allowLength = Integer.valueOf(type.substring(leftPos+1,rightPos-1));
                    }
                    if( fieldValue.toString().length() > allowLength){
                        return fieldDisplayName + "太长！最大长度为" + allowLength;
                    }

                }
                //if (!fieldColumnMeta.unique()) {
                if (!"PRI".equalsIgnoreCase((String)columnMeta.get("Key"))) {
                    continue;
                }
                //当前字段要求唯一
                Map<String, Object> queryCondMap = new HashMap<>();
                queryCondMap.put(fieldName, fieldValue);
                List<Map> list = findByQueryCondition(tableName,queryCondMap);
                System.out.println("fieldName = "+ fieldName + ",fieldValue = "+fieldValue+", find list = " + list);
                Long curId = 0L;
                for (Map e : list) {
                    //获取数据库中数据的ID
                    curId = getIdFromEntity(e);
                    Object efv = e.get( fieldName);
                    if (curId != null && curId != 0
                            && curId != id) {
                        if( (fieldValue == null && efv == null)
                                ||  (fieldValue != null && efv != null && fieldValue.equals(efv))){
                            return fieldDisplayName + "不能重复！值为"+fieldValue;
                        }
                    }
                }
            }catch(Exception e){
                LogOperator.err(loggerName, "persistedObjectFieldDuplicated error", e);
            }
        }

        return null;
    }

    public String getFieldDisplayName(Map entityT,String fieldName){
        if(entityT == null){
            return fieldName;
        }
//        Field field = Reflections.getAccessibleField(entityT,fieldName);
//        if(field != null){
//            ExcelField fieldExcelFieldMeta = field.getAnnotation(ExcelField.class);
//            return fieldExcelFieldMeta == null ? fieldName: fieldExcelFieldMeta.title();
//        }
        return fieldName;
    }

    /**
     * 导入文件内容
     * @param file
     * @param request
     * @return
     */
    @Override
    public Result<Integer> fileImport(String tableName,MultipartFile file, HttpServletRequest request){
        return null;
    }
}